1 /*
2  * Collie - An asynchronous event-driven network framework using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd 
5  *
6  * Developer: putao's Dlang team
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 module collie.codec.http.server.requesthandler;
12 import kiss.logger;
13 import collie.codec.http.httpmessage;
14 import collie.codec.http.server.responsehandler;
15 import collie.codec.http.errocode;
16 import collie.codec.http.codec.wsframe;
17 import collie.codec.http.headers;
18 import collie.codec.http.httptansaction;
19 import collie.codec.http.server.responsebuilder;
20 import collie.codec.http.codec.httpcodec;
21 import collie.utils.string;
22 import kiss.event;
23 
24 abstract class RequestHandler
25 {
26 	void setResponseHandler(ResponseHandler handler) nothrow
27 	{
28 		_downstream = handler;
29 	}
30 
31 	@property ResponseHandler responseHandler()
32 	{
33 		return _downstream;
34 	}
35 
36 	/**
37    * Invoked when we have successfully fetched headers from client. This will
38    * always be the first callback invoked on your handler.
39    */
40 	void onRequest(HttpMessage headers) nothrow
41 	{
42 	}
43 
44 	/**
45 	deprecated("Incorrect spelling. Using onRequest instead.")
46 	*/
47 	void onResquest(HttpMessage headers) nothrow
48 	{
49 		onRequest(headers);
50 	}
51 
52 	/**
53    * Invoked when we get part of body for the request.
54    */
55 	void onBody(const ubyte[] data) nothrow;
56 
57 	/**
58    * Invoked when we finish receiving the body.
59    */
60 	void onEOM() nothrow;
61 
62 	/**
63    * Invoked when request processing has been completed and nothing more
64    * needs to be done. This may be a good place to log some stats and
65    * clean up resources. This is distinct from onEOM() because it is
66    * invoked after the response is fully sent. Once this callback has been
67    * received, `downstream_` should be considered invalid.
68    */
69 	void requestComplete() nothrow;
70 
71 	/**
72    * Request failed. Maybe because of read/write error on socket or client
73    * not being able to send request in time.
74    *
75    * NOTE: Can be invoked at any time (except for before onRequest).
76    *
77    * No more callbacks will be invoked after this. You should clean up after
78    * yourself.
79    */
80 	void onError(HTTPErrorCode code) nothrow;
81 
82 	void onFrame(ref WSFrame frame) nothrow
83 	{
84 	}
85 
86 	bool onUpgtade(CodecProtocol protocol, HttpMessage msg) nothrow
87 	{
88 		return false;
89 	}
90 
91 protected:
92 	ResponseHandler _downstream;
93 }
94 
95 final class RequestHandlerAdaptor : ResponseHandler, HTTPTransactionHandler
96 {
97 	this(RequestHandler handler)
98 	{
99 		super(handler);
100 	}
101 
102 	override void setTransaction(HTTPTransaction txn)
103 	{
104 		_txn = txn;
105 		_upstream.setResponseHandler(this);
106 	}
107 
108 	override void detachTransaction()
109 	{
110 		_txn = null;
111 		if (!_erro)
112 		{
113 			_upstream.requestComplete();
114 		}
115 	}
116 
117 	override void onHeadersComplete(HttpMessage msg)
118 	{
119 		// logDebug("onHeadersComplete , erro is : ", _erro , " _upstream is ", cast(void *)_upstream);
120 		if (msg.getHeaders.exists(HTTPHeaderCode.EXPECT))
121 		{
122 			logDebug("has header EXPECT--------");
123 			string str = msg.getHeaders.getSingleOrEmpty(HTTPHeaderCode.EXPECT);
124 			if (!isSameIngnoreLowUp(str, "100-continue"))
125 			{
126 				scope HttpMessage headers = new HttpMessage();
127 				headers.constructDirectResponse(1, 1, 417, "Expectation Failed");
128 				headers.wantsKeepAlive(false);
129 				_txn.sendHeadersWithEOM(headers);
130 				return;
131 			}
132 			else
133 			{
134 				scope HttpMessage headers = new HttpMessage();
135 				headers.constructDirectResponse(1, 1, 100, "Continue");
136 				_txn.sendHeaders(headers);
137 			}
138 		}
139 		if (!_erro)
140 			_upstream.onResquest(msg);
141 	}
142 
143 	override void onBody(const ubyte[] chain)
144 	{
145 		_upstream.onBody(chain);
146 	}
147 
148 	override void onChunkHeader(size_t lenght)
149 	{
150 	}
151 
152 	override void onChunkComplete()
153 	{
154 	}
155 
156 	override void onEOM()
157 	{
158 		if (!_erro)
159 			_upstream.onEOM();
160 	}
161 
162 	override void onError(HTTPErrorCode erromsg)
163 	{
164 		if (_erro)
165 			return;
166 		version(CollieDebugMode) {
167 			import std.conv;
168 			warning(to!string(erromsg));
169 		}
170 		_erro = true;
171 		_upstream.onError(erromsg);
172 	}
173 
174 	override void onWsFrame(ref WSFrame wsf)
175 	{
176 		_upstream.onFrame(wsf);
177 	}
178 
179 	override void onEgressPaused()
180 	{
181 	}
182 
183 	override void onEgressResumed()
184 	{
185 	}
186 
187 	override void sendHeadersWithEOM(HttpMessage msg)
188 	{
189 		if (_txn)
190 			_txn.sendHeadersWithEOM(msg);
191 	}
192 
193 	override void sendHeaders(HttpMessage msg)
194 	{
195 		_responseStarted = true;
196 		if (_txn)
197 			_txn.sendHeaders(msg);
198 	}
199 
200 	override void sendChunkHeader(size_t len)
201 	{
202 		if (_txn)
203 			_txn.sendChunkHeader(len);
204 	}
205 
206 	override void sendBody(in ubyte[] data, bool iseom = false)
207 	{
208 		if (_txn)
209 			_txn.sendBody(data, iseom);
210 		if (iseom)
211 			_responseStarted = false;
212 	}
213 
214 	override void sendChunkTerminator()
215 	{
216 		if (_txn)
217 			_txn.sendChunkTerminator();
218 	}
219 
220 	override void sendEOM()
221 	{
222 		if (_txn)
223 			_txn.sendEOM();
224 		_responseStarted = false;
225 	}
226 
227 	override void sendTimeOut()
228 	{
229 		if (_txn)
230 			_txn.sendTimeOut();
231 	}
232 
233 	override void socketWrite(StreamWriteBuffer buffer)
234 	{
235 		if (_txn)
236 			_txn.socketWrite(buffer);
237 	}
238 
239 	override void sendWsData(OpCode code, ubyte[] data)
240 	{
241 		if (_txn)
242 			_txn.sendWsData(code, data);
243 	}
244 
245 	override bool onUpgtade(CodecProtocol protocol, HttpMessage msg)
246 	{
247 		return _upstream.onUpgtade(protocol, msg);
248 	}
249 
250 private:
251 	HTTPTransaction _txn;
252 	bool _erro = false;
253 	bool _responseStarted = false;
254 }